package cn.spike.user.service.impl;

import cn.spike.basic.exception.BusinessException;
import cn.spike.basic.jwt.JwtUtils;
import cn.spike.basic.jwt.LoginData;
import cn.spike.basic.jwt.RsaUtils;
import cn.spike.basic.util.AjaxResult;
import cn.spike.basic.util.HttpUtil;
import cn.spike.basic.util.Md5Utils;
import cn.spike.basic.util.StrUtils;
import cn.spike.system.domain.Menu;
import cn.spike.user.constant.WxConstants;
import cn.spike.user.domain.Logininfo;
import cn.spike.user.domain.User;
import cn.spike.user.domain.Wxuser;
import cn.spike.user.dto.LoginDto;
import cn.spike.user.mapper.LogininfoMapper;
import cn.spike.user.mapper.UserMapper;
import cn.spike.user.mapper.WxuserMapper;
import cn.spike.user.service.ILogininfoService;
import cn.spike.basic.service.impl.BaseServiceImpl;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        //校验
        //1.空值校验
        if (StringUtils.isEmpty(loginDto.getAccount()) || StringUtils.isEmpty(loginDto.getCheckPass())) {
            throw new BusinessException("请填写登录信息");
        }
        //2.账号是否正确
        Logininfo logininfo = logininfoMapper.loadByAccount(loginDto);
        if (logininfo == null) {
            throw new BusinessException("账号错误");
        }
        //3.密码是否正确
        String inputPassword = Md5Utils.encrypByMd5(loginDto.getCheckPass() + logininfo.getSalt());
        if (!inputPassword.equals(logininfo.getPassword())) {
            throw new BusinessException("密码错误");
        }
        //4.校验是否禁用
        if (!logininfo.getDisable()) {
            throw new BusinessException("该账户已被禁用,请联系管理员");
        }

        return loginSuccessJwtHandler(logininfo);
    }

    @Override
    public AjaxResult wechatLogin(Map<String, String> map) {
        //发送第二个请求需要 发送地址,code,APPID
        //准备第二个请求的地址 - 替换掉常量地址中写死的参数
        String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET).replace("CODE", map.get("code"));
        //使用HttpUtil发送微信登录请求：第二个请求返回的是Json格式的字符串
        String jsonStr = HttpUtil.httpGet(url);
        //使用FastJson将Json格式字符串转换为Json对象：为了获取access_token和openId
        JSONObject jsonObj = JSONObject.parseObject(jsonStr);
        //用getString方法获取参数 - access_token和openid - 如果第一次扫码 - 响应这两个参数给前端
        String access_token = jsonObj.getString("access_token");
        String openid = jsonObj.getString("openid");

        //判断是第一次扫码绑定还是后续扫码登陆
        Wxuser wxuser = wxuserMapper.loadByOpenId(openid);
        if (wxuser != null) { //如果通过openid查询到了信息 - 说明后续扫码登录
            User user = userMapper.loadById(wxuser.getUserId());
            //并判断wxuser是否正确绑定了user信息 - 避免误删操作引起的bug
            if (user != null) {
                //判断正确 - 生成token和查询出登录信息
                //保存到redis中 - 并将token和logininfo传递到前端
                Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

                Map<String, Object> resultMap = loginSuccessJwtHandler(logininfo);

                return AjaxResult.me().setObj(resultMap);
            }
        }
        //第一次扫码 - 返回地址栏的字符串?access_token=access_token&openid=openid
        //方便前端发第三次请求是传递参数 - 直接拼接在地址栏
        return AjaxResult.me().setSuccess(false).setObj("?access_token=" + access_token + "&openid=" + openid);
    }

    @Override
    public AjaxResult wechatBinder(Map<String, String> map) {
        String phone = map.get("phone");
        String verifyCode = map.get("verifyCode");
        String access_token = map.get("access_token");
        String openid = map.get("openid");
        //1.校验-空值
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)) {
            throw new BusinessException("登录信息不能为空");
        }

        //2.校验-手机验证码：是否过期，是否正确
        Object obj = redisTemplate.opsForValue().get("binder:" + phone);
        if (obj == null) {
            throw new BusinessException("验证码已失效，请重新获取");
        }
        if (!verifyCode.equals(obj.toString().split(":")[0])) {
            throw new BusinessException("验证码错误!!");
        }

        //3.准备第三个请求
        String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", access_token).replace("OPENID", openid);
        //4.使用HttpUtil发送微信绑定请求 - 获取微信用户信息：返回jsonStr
        String jsonStr = HttpUtil.httpGet(url);
        //5.使用fastJson处理jsonStr - 将jsonObj中的数据转换WxUser对象
        Wxuser wxuser = jsonStr2Wxuser(jsonStr);
        //6.创建User对象和Logininfo对象【phone】 - 难度
        User user = userMapper.loadByPhone(phone);
        if (user == null) {
            //7.添加数据库：Logininfo对象 - User对象 - WxUser对象
            user = wxuser2User(wxuser);
            user.setUsername(phone);
            user.setPhone(phone);

            Logininfo oldLogininfo = user2Logininfo(user);

            logininfoMapper.add(oldLogininfo);
            user.setLogininfoId(oldLogininfo.getId());
            userMapper.add(user);
        }
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

        Map<String, Object> resultMap = loginSuccessJwtHandler(logininfo);

        return AjaxResult.me().setObj(resultMap);
    }

    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        logininfo.setDisable(true);
        return logininfo;
    }

    private User wxuser2User(Wxuser wxuser) {
        User user = new User();
        String salt = StrUtils.getComplexRandomString(32);
        String randomPwd = StrUtils.getComplexRandomString(12);
        String password = Md5Utils.encrypByMd5(randomPwd + salt);
        user.setSalt(salt);
        user.setPassword(password);
        user.setState(1);
        return user;
    }

    private Wxuser jsonStr2Wxuser(String jsonStr) {
        Wxuser wxuser = new Wxuser();
        //用fastjson将json字符串转换成对象
        JSONObject jsonObj = JSONObject.parseObject(jsonStr);
        String openid = jsonObj.getString("openid");
        String nickname = jsonObj.getString("nickname");
        String headimgurl = jsonObj.getString("headimgurl");
        String unionid = jsonObj.getString("unionid");
        wxuser.setNickname(nickname);
        wxuser.setOpenid(openid);
        wxuser.setHeadimgurl(headimgurl);
        wxuser.setUnionid(unionid);
        return wxuser;
    }

    private Map<String,Object> loginSuccessJwtHandler(Logininfo logininfo){
        //创建一个map 返回给前端 用来做前端的信息显示和权限
        Map<String,Object> map =new HashMap<>();
        //loginData作为登录信息权限信息和菜单权限的数据类 - 加密时作为载荷 - 后续登录拦截不用强转
        LoginData loginData = new LoginData();

        //将密码和盐值置空 - 提高安全性
        logininfo.setSalt(null);
        logininfo.setPassword(null);

        //将登录信息放到map中作为前端显示返回
        map.put("logininfo",logininfo);
        loginData.setLogininfo(logininfo);

        //如果登录信息的type属性==0 说明是管理员登录 还要设置资源权限和菜单权限信息
        if(logininfo.getType()==0){
            //获取到当前登陆人的所有资源权限
            List<String> permissions = logininfoMapper.findPermissionsByLogininfoId(logininfo.getId());
            map.put("permissions",permissions);
            loginData.setPermissions(permissions);

            //获取到当前登录人的所有菜单权限
            List<Menu> menus= logininfoMapper.findMenusByLogininfoId(logininfo.getId());
            map.put("menus",menus);
            loginData.setMenus(menus);
        }

        //使用JWT进行加密
        //1.获取到私钥对象
        try {
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            //将传输信息loginData进行加密 - 并设置30分钟过期时间
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 1440);
            //后续前端将token传回 解密获取登录信息
            map.put("token",jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //将登陆信息返回至前端
        return map;
    }


}
